# -*- coding: UTF-8 -*-
####################################################
## 4. 函数
####################################################

# 用 def 来新建函数
def add(x, y):
    print("x is %s and y is %s" % (x, y))
    return x + y    # 通过 return 来返回值

# 调用带参数的函数
add(5, 6)  # => 输出 "x is 5 and y is 6" 返回 11

# 通过关键字赋值来调用函数
add(y=6, x=5)   # 顺序是无所谓的

# 我们也可以定义接受多个变量的函数，这些变量是按照顺序排列的
def varargs(*args):
    return args

varargs(1, 2, 3)  # => (1,2,3)


# 我们也可以定义接受多个变量的函数，这些变量是按照关键字排列的
def keyword_args(**kwargs):
    return kwargs

# 实际效果：
keyword_args(big="foot", loch="ness")  # => {"big": "foot", "loch": "ness"}

# 你也可以同时将一个函数定义成两种形式
def all_the_args(*args, **kwargs):
    print(args)
    print(kwargs)


"""
all_the_args(1, 2, a=3, b=4) prints:
    (1, 2)
    {"a": 3, "b": 4}
"""

# 当调用函数的时候，我们也可以进行相反的操作，把元组和字典展开为参数
args = (1, 2, 3, 4)
kwargs = {"a": 3, "b": 4}
all_the_args(*args)  # 等价于 foo(1, 2, 3, 4)
all_the_args(**kwargs)  # 等价于 foo(a=3, b=4)
all_the_args(*args, **kwargs)  # 等价于 foo(1, 2, 3, 4, a=3, b=4)

# 函数在 python 中是一等公民
def create_adder(x):
    def adder(y):
        return x + y
    return adder

add_10 = create_adder(10)
add_10(3)  # => 13

# 匿名函数
(lambda x: x > 2)(3)  # => True

# 内置高阶函数
map(add_10, [1, 2, 3])  # => [11, 12, 13]
filter(lambda x: x > 5, [3, 4, 5, 6, 7])  # => [6, 7]

# 可以用列表方法来对高阶函数进行更巧妙的引用
[add_10(i) for i in [1, 2, 3]]  # => [11, 12, 13]
[x for x in [3, 4, 5, 6, 7] if x > 5]  # => [6, 7]
(x for x in [3, 4, 5, 6, 7] if x > 5)

# 我的练习
def func(arg1, arg2="default", *arg3, **arg4):
    print("arg1: " + arg1)
    print("arg2: " + arg2)
    print("arg3: " + str(arg3))
    print("arg4: " + str(arg4))
    print("----------")

func("a1")
func(arg1="a1")

func("a1", "a2")
func(arg1="a1", arg2="a2")
# func(arg1="a1", "a2")  # error: positional argument follows keyword argument
func("a1", arg2="a2")
func("a1", "2", "3", fuck="f") # arg1: a1 arg2: 2 arg3: ('3',) arg4: {'fuck': 'f'}

####################################################
## 5. 类
####################################################

# 我们新建的类是从 object 类中继承的
class Human(object):

     # 类属性，由所有类的对象共享
    species = "H. sapiens"

    # 基本构造函数
    def __init__(self, name):
        # 将参数赋给对象成员属性
        self.name = name

    # 成员方法，参数要有 self
    def say(self, msg):
        return "%s: %s" % (self.name, msg)

    # 类方法由所有类的对象共享
    # 这类方法在调用时，会把类本身传给第一个参数
    @classmethod
    def get_species(cls):
        return cls.species

    # 静态方法是不需要类和对象的引用就可以调用的方法
    @staticmethod
    def grunt():
        return "*grunt*"


# 实例化一个类
i = Human(name="Ian")
print(i.say("hi"))  # 输出 "Ian: hi"

j = Human("Joel")
print(j.say("hello"))  # 输出 "Joel: hello"

# 访问类的方法
i.get_species()  # => "H. sapiens"

# 改变共享属性
Human.species = "H. neanderthalensis"
i.get_species()  # => "H. neanderthalensis"
j.get_species()  # => "H. neanderthalensis"

# 访问静态变量
Human.grunt()  # => "*grunt*"


####################################################
## 6. 模块
####################################################

# 我们可以导入其他模块
import math
print(math.sqrt(16))  # => 4

# 我们也可以从一个模块中导入特定的函数
from math import ceil, floor
print(ceil(3.7))  # => 4.0
print(floor(3.7))  # => 3.0

# 从模块中导入所有的函数
# 警告：不推荐使用
from math import *

# 简写模块名
import math as m
math.sqrt(16) == m.sqrt(16)  # => True

# Python的模块其实只是普通的python文件
# 你也可以创建自己的模块，并且导入它们
# 模块的名字就和文件的名字相同

# 也可以通过下面的方法查看模块中有什么属性和方法
import math
dir(math)



